package cn.virens.common.boot.filter.logging;

import cn.virens.common.util.exception.APIException;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.dromara.hutool.core.io.IoUtil;
import org.springframework.web.util.WebUtils;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;

public class SimpleHandlerLoggingRequest extends HttpServletRequestWrapper {
    private ByteArrayOutputStream cache = new ByteArrayOutputStream();

    public SimpleHandlerLoggingRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new LogServletInputStream(super.getInputStream());
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return IoUtil.toReader(getInputStream(), getCharacter());
    }

    public Charset getCharacter() throws APIException {
        return Charset.forName(getCharacterEncoding());
    }

    public byte[] getCacheAsByteArray() {
        return cache.toByteArray();
    }

    @Override
    public String getCharacterEncoding() throws APIException {
        String character = super.getCharacterEncoding();
        if (character == null || character.isEmpty()) {
            return WebUtils.DEFAULT_CHARACTER_ENCODING;
        }

        return character;
    }

    public class LogServletInputStream extends ServletInputStream {
        private ServletInputStream inputStream;

        public LogServletInputStream(ServletInputStream inputStream) {
            this.inputStream = inputStream;
        }

        @Override
        public boolean isReady() {
            return inputStream.isReady();
        }

        @Override
        public boolean isFinished() {
            return inputStream.isFinished();
        }

        @Override
        public void setReadListener(ReadListener readListener) {
            inputStream.setReadListener(readListener);
        }

        @Override
        public int read() throws IOException {
            int result = inputStream.read();
            if (result > -1) {
                cache.write(result);
            }

            return result;
        }

        @Override
        public int read(byte[] b) throws IOException {
            return writeToCache(b, 0, inputStream.read(b));
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            return writeToCache(b, off, inputStream.read(b, off, len));
        }

        @Override
        public int readLine(byte[] b, int off, int len) throws IOException {
            return writeToCache(b, off, inputStream.readLine(b, off, len));
        }

        private int writeToCache(byte[] b, int off, int len) {
            if (b != null && off >= 0 && len > 0) {
                cache.write(b, off, len);
            }

            return len;
        }

    }

}
